package com.tonicgb.api.admin.controller;


import com.tonicgb.shared.exception.ApiException;
import com.tonicgb.shared.common.response.Result;
import org.springframework.boot.autoconfigure.web.ErrorProperties;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.Assert;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.context.request.WebRequest;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;

/**
 * @author zhaiyujin
 * @date 2021-09-01 14:08:40
 * @说明 覆盖系统异常
 */
@RequestMapping("${server.error.path:${error.path:/error}}")
public class MyCustomErrorController implements ErrorController {

    private final ErrorProperties errorProperties;
    private final ErrorAttributes errorAttributes;


    public MyCustomErrorController(ErrorAttributes errorAttributes, ErrorProperties errorProperties) {
        Assert.notNull(errorProperties, "ErrorProperties must not be null");
        this.errorProperties = errorProperties;
        this.errorAttributes = errorAttributes;
    }
    @Override
    public String getErrorPath() {
        return this.errorProperties.getPath();
    }

    private Result validationErrorResult(BindingResult bindingResult) {
        String message = bindingResult.getAllErrors().get(0).getDefaultMessage();
//                .stream()
//                .map(DefaultMessageSourceResolvable::getDefaultMessage)
//                .collect(Collectors.joining(","));
        return Result.error(Result.VALIDATE_ERROR, message);
    }

    @RequestMapping
    public ResponseEntity error(HttpServletRequest request) {
        HttpStatus httpStatus = getStatus(request);
        WebRequest webRequest = new ServletWebRequest(request);
        Throwable error = errorAttributes.getError(webRequest);
        Result result = Result.error(Result.ERROR, httpStatus.toString());
        if (error != null) {
            if (error instanceof BindException) {
                result = validationErrorResult(((BindException) error).getBindingResult());
            } else if (error instanceof ConstraintViolationException) {
                result = validationErrorResult((ConstraintViolationException) error);
            } else if (error instanceof MethodArgumentNotValidException) {
                result = validationErrorResult(((MethodArgumentNotValidException) error).getBindingResult());
            } else if (error instanceof ApiException) {
                ApiException apiException = (ApiException) error;
                if (apiException.getErrorCode() == 1) {
                    result = Result.error(Result.ERROR, apiException.getMessage());
                } else {
                    result = Result.error(apiException.getErrorCode(), apiException.getMessage());
                }
            } else {
                result = Result.error(Result.ERROR, Result.SYSTEM_BUSSY);
            }
        }
        return new ResponseEntity<>(result, HttpStatus.OK);
    }

    private Result validationErrorResult(ConstraintViolationException exception) {
        ConstraintViolation<?> constraintViolation = exception.getConstraintViolations().stream().findFirst().orElse(null);
        if (constraintViolation != null) {
            return Result.error(Result.VALIDATE_ERROR, constraintViolation.getMessage());
        }
//                .stream()
//                .map(ConstraintViolation::getMessage)
//                .collect(Collectors.joining(","));
        return Result.error(Result.VALIDATE_ERROR, "");
    }
    private HttpStatus getStatus(HttpServletRequest request) {
        Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
        if (statusCode == null) {
            return HttpStatus.INTERNAL_SERVER_ERROR;
        }
        try {
            return HttpStatus.valueOf(statusCode);
        } catch (Exception ex) {
            return HttpStatus.INTERNAL_SERVER_ERROR;
        }
    }
}
